use std::collections::HashMap;

use chrono::NaiveDateTime;
use sea_orm::ActiveValue::NotSet;
use sea_orm::{
    ActiveModelTrait, ColumnTrait, DbConn, DerivePartialModel, EntityTrait, FromQueryResult,
    PartialModelTrait, QueryFilter, QueryOrder, QuerySelect, QueryTrait, Set, TransactionTrait,
};
use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::entity::sys_dept;
type Result<T> = std::result::Result<T, sea_orm::DbErr>;

use crate::entity::sys_dept::Entity as SystemDept;
use crate::util;

#[derive(Debug, DerivePartialModel, FromQueryResult)]
#[sea_orm(entity = "SystemDept")]
pub struct PartialUser {
    pub dept_id: i32,
}

pub struct Dept;

// 单位详细的数据展示
#[derive(Debug, DerivePartialModel, FromQueryResult, Serialize)]
#[sea_orm(entity = "SystemDept")]
pub struct DeptDetailVo {
    pub dept_id: i32,
    pub parent_id: i32,
    pub dept_name: String,
    pub order_num: i32,
    pub description: String,
    pub status: bool,
    pub created_at: NaiveDateTime,
    pub updated_at: NaiveDateTime,
}

// 单位列表的数据展示
#[derive(Debug, DerivePartialModel, FromQueryResult, Serialize)]
#[sea_orm(entity = "SystemDept")]
pub struct DeptListVo {
    pub dept_id: i32,
    pub parent_id: i32,
    pub dept_name: String,
    pub order_num: i32,
    pub status: bool,
    pub created_at: NaiveDateTime,
}

impl From<sys_dept::Model> for DeptListVo {
    fn from(value: sys_dept::Model) -> Self {
        Self {
            dept_id: value.dept_id,
            parent_id: value.parent_id,
            dept_name: value.dept_name,
            order_num: value.order_num,
            status: value.status,
            created_at: value.created_at,
        }
    }
}

// 单位树形结构的数据
#[derive(Debug, Serialize)]
pub struct DeptTreeVo {
    #[serde(flatten)]
    pub dept: DeptListVo,
    pub children: Option<Vec<DeptTreeVo>>,
}

/// 单位提交的数据
#[derive(Debug, FromQueryResult, Deserialize, Validate)]
pub struct DeptDo {
    pub parent_id: Option<i32>,
    #[validate(length(min = 3, max = 32), non_control_character)]
    pub dept_name: String,
    pub phone: Option<String>,
    pub email: Option<String>,
    pub description: Option<String>,
    #[serde(default)]
    pub order_num: Option<i32>,
    pub status: bool,
}

impl Dept {
    /// 根据id查找部门
    #[inline]
    pub async fn fetch_one<T>(
        conn: &DbConn,
        dept_id: i32,
        status: Option<bool>,
        del: Option<bool>,
    ) -> Result<Option<T>>
    where
        T: PartialModelTrait + Send + Sync,
    {
        sys_dept::Entity::find_by_id(dept_id)
            .apply_if(status, |sql, status| {
                sql.filter(sys_dept::Column::Status.eq(status))
            })
            .apply_if(del, |sql, del| {
                sql.filter(sys_dept::Column::DelFlag.eq(del))
            })
            .into_partial_model()
            .one(conn)
            .await
    }

    /// 根据id获得所有子部门
    pub async fn get_child_dept_list(conn: &DbConn, dept_id: i32) -> Result<Vec<DeptListVo>> {
        sys_dept::Entity::find()
            .select_only()
            .column(sys_dept::Column::DeptName)
            .filter(sys_dept::Column::ParentId.eq(dept_id))
            .order_by_asc(sys_dept::Column::OrderNum)
            .into_partial_model()
            .all(conn)
            .await
    }

    pub async fn create(db: &DbConn, data: DeptDo) -> Result<()> {
        let p_id = data.parent_id.unwrap();
        let ancestors = if p_id == 0 {
            "/".to_string()
        } else {
            sys_dept::Entity::find_by_id(p_id)
                .select_only()
                .column(sys_dept::Column::Ancestors)
                .into_tuple::<String>()
                .one(db)
                .await?
                .unwrap()
        };
        let ancestors = format!("{}{}/", ancestors, p_id);
        let active = sys_dept::ActiveModel {
            parent_id: Set(p_id),
            ancestors: Set(ancestors),
            dept_name: Set(data.dept_name),
            phone: Set(data.phone.unwrap_or("".to_owned())),
            email: Set(data.email.unwrap_or("".to_owned())),
            description: Set(data.description.unwrap_or("".to_owned())),
            order_num: Set(data.order_num.unwrap_or(0)),
            status: Set(true),
            del_flag: Set(false),
            dept_id: NotSet,
            manager: Set("".to_owned()),
            created_at: Set(util::current_datetime()),
            updated_at: Set(util::current_datetime()),
        };
        active.insert(db).await?;
        Ok(())
    }

    pub async fn update(db: &DbConn, id: i32, data: DeptDo) -> Result<bool> {
        let (p_id, ancestors) = sys_dept::Entity::find_by_id(id)
            .select_only()
            .columns([sys_dept::Column::ParentId, sys_dept::Column::Ancestors])
            .into_tuple::<(i32, String)>()
            .one(db)
            .await?
            .unwrap();
        let new_p_id = data.parent_id.unwrap_or(p_id);
        if new_p_id == id {
            return Ok(false);
        }

        let mut active = sys_dept::ActiveModel {
            dept_id: Set(id),
            dept_name: Set(data.dept_name),
            phone: data.phone.map(Set).unwrap_or(NotSet),
            email: data.email.map(Set).unwrap_or(NotSet),
            description: data.description.map(Set).unwrap_or(NotSet),
            order_num: data.order_num.map(Set).unwrap_or(NotSet),
            status: Set(data.status),
            del_flag: Set(false),
            updated_at: Set(util::current_datetime()),
            ..Default::default()
        };
        let mut actives = Vec::new();
        // 父子关系变化后, 更新ancestors
        if new_p_id != p_id {
            let t = format!("/{new_p_id}/");
            // 能从原父子关系，找到
            let new_ancestors = if let Some(pos) = ancestors.find(&t) {
                ancestors[0..pos + t.len()].to_owned()
            } else {
                // 重新获取
                let ancestors = if new_p_id == 0 {
                    "/".to_string()
                } else {
                    sys_dept::Entity::find_by_id(new_p_id)
                        .select_only()
                        .column(sys_dept::Column::Ancestors)
                        .into_tuple::<String>()
                        .one(db)
                        .await?
                        .unwrap()
                };
                format!("{}{}/", ancestors, new_p_id)
            };

            // 检查是否成环
            if new_ancestors.find(&format!("/{id}/")).is_some() {
                return Ok(false);
            }

            active.parent_id = Set(new_p_id);
            active.ancestors = Set(new_ancestors.clone());

            let childs = sys_dept::Entity::find()
                .select_only()
                .columns([sys_dept::Column::DeptId, sys_dept::Column::Ancestors])
                .filter(sys_dept::Column::Ancestors.starts_with(format!("{ancestors}{id}/")))
                .into_tuple::<(i32, String)>()
                .all(db)
                .await?;
            childs.into_iter().for_each(|f| {
                let ancestors = format!("{}{}", new_ancestors, f.1[ancestors.len()..].to_owned());
                actives.push(sys_dept::ActiveModel {
                    dept_id: Set(f.0),
                    ancestors: Set(ancestors),
                    ..Default::default()
                })
            })
        }
        let txn = db.begin().await?;
        active.update(&txn).await?;
        for child in actives {
            child.update(&txn).await?;
        }
        txn.commit().await?;
        Ok(true)
    }

    /// 软删除一个单位, 单位不存在抛出错误
    pub async fn delete(db: &DbConn, id: i32) -> Result<sys_dept::Model> {
        let model = sys_dept::ActiveModel {
            dept_id: Set(id),
            del_flag: Set(true),
            updated_at: Set(util::current_datetime()),
            ..Default::default()
        };
        sys_dept::Entity::update(model).exec(db).await
    }

    /// 获得一个部门的树结构
    /// - id 1 为全部树
    /// - status 是否过滤 未启用的，true 表示过滤
    pub async fn get_tree(db: &DbConn, id: i32, status: bool) -> Result<Option<DeptTreeVo>> {
        let root = if let Some(root) = sys_dept::Entity::find_by_id(id).one(db).await? {
            root
        } else {
            return Ok(None);
        };
        let mut sql = sys_dept::Entity::find()
            .filter(sys_dept::Column::Ancestors.starts_with(format!("{}{id}/", root.ancestors)))
            .filter(sys_dept::Column::DelFlag.eq(false))
            .order_by_asc(sys_dept::Column::OrderNum);
        if status {
            sql = sql.filter(sys_dept::Column::Status.eq(true))
        }
        let childs = sql.into_partial_model::<DeptListVo>().all(db).await?;
        let mut childs_map: HashMap<i32, Vec<DeptListVo>> = HashMap::new();
        for ele in childs {
            if let Some(d) = childs_map.get_mut(&ele.parent_id) {
                d.push(ele);
            } else {
                childs_map.insert(ele.parent_id, vec![ele]);
            }
        }
        let childs = recursion_tree(childs_map, &id).1;
        let data = DeptTreeVo {
            dept: root.into(),
            children: childs,
        };
        Ok(Some(data))
    }
}

fn recursion_tree(
    mut data: HashMap<i32, Vec<DeptListVo>>,
    id: &i32,
) -> (HashMap<i32, Vec<DeptListVo>>, Option<Vec<DeptTreeVo>>) {
    if let Some(childs) = data.remove(&id) {
        let mut tmp = Vec::new();
        for c in childs {
            let d = recursion_tree(data, &c.dept_id);
            data = d.0;
            tmp.push(DeptTreeVo {
                dept: c,
                children: d.1,
            });
        }
        (data, Some(tmp))
    } else {
        (data, None)
    }
}
